using System;

namespace ClieOp.Record
{
    internal class Field
    {
        private object _value;

        public Field(FieldName name, FieldType type, int length, object value)
        {
            Name = name;
            Type = type;
            Length = length;
            Value = value;
            IsReadOnly = true;
        }

        public Field(FieldName name, FieldType type, int length)
        {
            Name = name;
            Type = type;
            Length = length;
            IsReadOnly = false;
        }

        public FieldName Name { get; private set; }
        public int Length { get; private set; }
        public FieldType Type { get; private set; }

        public object Value
        {
            get { return _value ?? (Type == FieldType.Type9 ? (object)0L : ""); }
            set
            {
                if (IsReadOnly)
                {
                    throw new Exception("Cannot set value on read-only field.");
                }
                _value = value;
            }
        }

        public bool IsReadOnly { get; private set; }

        public new string ToString()
        {
            return Type == FieldType.Type9 ? Utility.ClieOpNum(Value.ToString(), Length) : Utility.ClieOpString(Value.ToString(), Length);
        }

        public static string operator +(Field field1, Field field2)
        {
            return field1.ToString() + field2.ToString();
        }

        public static string operator +(string field1, Field field2)
        {
            return field1 + field2.ToString();
        }

        public static string operator +(Field field1, string field2)
        {
            return field1.ToString() + field2;
        }

        public static bool operator ==(Field field1, object value)
        {
            return !ReferenceEquals(null, field1) && field1.Equals(value);
        }

        public static bool operator !=(Field field1, object value)
        {
            return !(field1 == value);
        }

        public override bool Equals(object obj)
        {
            return !ReferenceEquals(null, obj)
                   &&
                   (ReferenceEquals(this, obj)
                    ||
                    (obj.GetType() == typeof (Field)
                         ? Equals((Field)obj)
                         : ToString() == (Type == FieldType.Type9 ? Utility.ClieOpNum(obj.ToString(), Length) : Utility.ClieOpString(obj.ToString(), Length))));
        }

        public bool Equals(Field field)
        {
            return !ReferenceEquals(null, field)
                   && (ReferenceEquals(this, field) || (field.Name == Name && field.Length == Length && field.Type == Type && field.IsReadOnly == IsReadOnly));
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = (Name.GetHashCode() * 397) ^ Length;
                result = (result * 397) ^ Type.GetHashCode();
                result = (result * 397) ^ IsReadOnly.GetHashCode();
                return result;
            }
        }
    }
}